

(function (global, factory) {
	if (typeof module === "object" && typeof module.exports === "object") {
		module.exports = factory(global);
	} else {
		factory(global);
	}
})(typeof window !== "undefined" ? window : this, function (global) {
	'use strict';

	var extendObj = {
		config: {
			//默认的数据类型
			defaultValueConvert: 'string',
			//非表单元素时，默认取值的attr
			defaultAttr: '',
			//默认的数据类型
			defaultValuePick: 'pick',
			combineStart: 'e',
			combineMiddle: '-',
			combineEnd: 'combine',
			startParentTag: '\\{',
			endParentTag: '\\}',
			startArrayTag: '\\[',
			endArrayTag: '\\]',
			syntacticEndingChar: ';',
			syntacticMultiChar: ',',
			syntacticDeliveryChar: '->',
			syntacticRelyChar: '@',
			//convertCommand和pickCommand在页面中的位置交换
			isExchanged: false,
			//空的json或array是或采集
			isEmptyObjectKept: false,
			//存储内部信息的字段名
			occupation: '__combine__',
			//数据中的combine信息是否可以删除
			occupationConfigurable: false,
			query: function (tar, attr) {
				return tar.querySelectorAll('[' + attr + ']');
			},
			querySkip: notChildCombineDom,
			getProp: function (tar, attr) {
				return tar.getAttribute(attr);
			},
			//当为真值时，不继续处理
			toContinue: function (val) {
				return isNullOrUndef(val);
			}
		},
		//工具方法集
		store: {},
		pickCommand: {
			pick: function (attr) {
				var target = this.target;
				var config = extendObj.config;
				var nodeName = target.nodeName.toLowerCase();
				//表单元素标签
				var arr = ['input', 'textarea', 'select', 'option', 'button'];
				var isFormEle = arr.indexOf(nodeName) > -1;
				if (isFormEle && (!attr || attr === 'value')) {
					return target.value;
				} else {
					attr = attr || config.defaultAttr || config.combineStart + '-value';
					return config.getProp(target, attr);
				}
			}
		},
		convertCommand: {
			//只保留基本的类型转换，其它“转换器”可自定义
			string: function (val) {
				return isNullOrUndef(val) ? val : String(val);
			},
			number: function (val) {
				return isNullOrUndef(val) ? val : Number(val);
			}
		},
		relyCommand: {
			rely: function (attr, val) {
				var target = this.target;
				var getProp = extendObj.config.getProp;
				var nodeName = target.nodeName.toLowerCase();
				if (
					//单、复选框
					(attr == 'checked' && (nodeName == 'input' && (target.type == 'radio' || target.type == 'checkbox')))
					||
					//option元素
					(attr == 'selected' && nodeName == 'option')
				) {
					var prop = target[attr];
					return val !== 'false' ? prop : !prop;
				} else {
					return getProp(target, attr) == val;
				}
			}
		},
		levelCommand: {
			join: function (arr, and) {
				if (!isArray(arr)) {
					error('请在数组项使用join方法')
				}
				return arr.join(isUndefined(and) ? ',' : and);
			}
		},
	};

	var rSplitEqualOrNo = createSplitRe('(!?=)', true);
	var rSplitOr = createSplitRe('|');
	//匹配函数指令
	var rCommand = /^([^\(\)\s]+)\s*\(?([^\(\)]*)\)?$/;
	//处理逻辑
	var rLogic = /([^&|\s])([^&|]*)([&|]?)/g;

	function combine(opt) {
		var config = extendObj.config;

		var target = opt.target;
		if (isUndefined(target)) {
			error('请传入target');
		}

		//全局指令方法对象
		var store = extendObj.store;
		var pickCommand = extendObj.pickCommand;
		var convertCommand = extendObj.convertCommand;
		var relyCommand = extendObj.relyCommand;
		var levelCommand = extendObj.levelCommand;
		//临时指令方法对象（临时传入的参数）
		var pickCommand_opt = opt.pickCommand || {};
		var convertCommand_opt = opt.convertCommand || {};
		var relyCommand_opt = opt.relyCommand || {};
		var levelCommand_opt = opt.levelCommand || {};


		var combineAttr = config.combineStart + config.combineMiddle + config.combineEnd;
		var toContinue = opt.query || config.toContinue;

		var query = opt.query || config.query;
		var querySkip = opt.querySkip || config.querySkip;
		var getProp = opt.getProp || config.getProp;

		var rSplitSemicolon = createSplitRe(config.syntacticEndingChar);
		var rSplitComma = createSplitRe(config.syntacticMultiChar);
		var rSplitAt = createSplitRe(config.syntacticRelyChar);
		var rSplitDelivery = createSplitRe(config.syntacticDeliveryChar);

		var occupation = config.occupation;
		var occupationConfigurable = config.occupationConfigurable;

		//父级combine对象正则
		var rParentObj = new RegExp('^' + config.startParentTag + '\\s*(.*?)\\s*' + config.endParentTag + '$');
		var rArrItem = new RegExp(config.startArrayTag + '(.*)' + config.endArrayTag);

		var defaultValueConvert = opt.defaultValueConvert || config.defaultValueConvert;
		var defaultValuePick = opt.defaultValuePick || config.defaultValuePick;
		var isExchanged = opt.isExchanged || config.isExchanged;
		var isEmptyObjectKept = opt.isEmptyObjectKept || config.isEmptyObjectKept;

		var objectChain = {};

		function createGetParent(data) {
			var obj;
			if (data && (obj = data[occupation])) {
				return function () {
					return obj.parent;
				};
			}
			error('createGetParent的第一个参数应该是comdine复杂数据');
		}

		var _combine = function (options) {
			options = options || {};
			var deep = options.deep || 0;
			var isChild = options.isChild;
			var parentData_combine = options.parent || null;
			var tar = options.target;

			var dataTypeIsArr = (options.dataType || 'json') === 'array';

			var tarCombineAttr = (getProp(tar, combineAttr) || '').trim();
			if (tarCombineAttr) {
				var parentProp = rParentObj.exec(tarCombineAttr);
				dataTypeIsArr = parentProp && rArrItem.test(parentProp[1]);
			}

			var combine_data = options.srcData || (dataTypeIsArr ? [] : {});
			if ((dataTypeIsArr && !isArray(combine_data)) || (!dataTypeIsArr && isArray(combine_data))) {
				error('srcData数据类型不正确');
			}

			function interrupt(isPropagation) {
				var interrupt = infoInCombineData.interrupt;
				interrupt.isInterrupt = true;
				interrupt.isPropagation = isPropagation === true;
			}

			function infoIntoCombineData(data, parentData) {
				var combineInfo = {
					interrupt: {
						isInterrupt: false,
						//是否影响父级数据的采集
						isPropagation: false
					},
					deep: deep,
					parent: parentData || (isChild ? parentData_combine : null),
				};
				Object.defineProperty(data, occupation, {
					get: function () {
						return combineInfo;
					},
					set: function () {//当用户combine名为occupation字段时
						error(occupation + '为内部使用字段名，换一个字段名吧');
					},
					configurable: occupationConfigurable,
				});

				return data[occupation];
			}

			function isInterrupt() {
				return infoInCombineData.interrupt.isInterrupt;
			}

			var infoInCombineData = infoIntoCombineData(combine_data);

			objectChain[deep++] = combine_data;

			var getParent = createGetParent(combine_data);

			function executeCommand(item, fn, fnName, args) {
				return fn.apply({
					interrupt: interrupt,
					store: store,
					relyCommand: relyCommand,
					convertCommand: convertCommand,
					pickCommand: pickCommand,
					levelCommand: levelCommand,
					data: combine_data,
					getParent: getParent,
					target: item,
					name: fnName,
				}, args);
			}

			each(query(tar, combineAttr), function (item) {
				if (isInterrupt()) {
					return false;
				}

				var attr_combine = null;

				if (querySkip(item, tar, combineAttr, rParentObj)) {
					return;
				}

				attr_combine = getProp(item, combineAttr).trim();
				var parentProp = rParentObj.exec(attr_combine);
				if (parentProp) {//父级元素
					var aPropInfo = parentProp[1].split(rSplitSemicolon);
					var arrCombine = rArrItem.exec(aPropInfo);
					var isArrayCombine = !!arrCombine;

					var levelCommandStr = aPropInfo[1] || '';

					var dataType = 'json';
					var property = aPropInfo[0];
					if (isArrayCombine) {
						property = arrCombine[1];
						dataType = 'array';
					}

					var sonOptions = {
						deep: deep,
						parent: combine_data,
						target: item,
						dataType: dataType,
						isChild: true,
					};

					var combine_data_son = _combine(sonOptions);
					var willInterrup;
					if (isExtendedObject(combine_data_son)) {
						var interruptInfo_sonData = combine_data_son[occupation].interrupt;
						willInterrup = interruptInfo_sonData.isInterrupt && interruptInfo_sonData.isPropagation;
					}

					each(levelCommandStr.split(rSplitDelivery), function (item) {
						if (item) {
							var oAfterCombineInfo = dealCommandStr(item);
							oAfterCombineInfo.args.unshift(combine_data_son);
							var fnConvert;
							var nameConvert = oAfterCombineInfo.name;
							if ((fnConvert = (levelCommand_opt[nameConvert] || levelCommand[nameConvert]))) {
								combine_data_son = executeCommand(item, fnConvert, nameConvert, oAfterCombineInfo.args);
								if (isInterrupt()) {
									return false;
								}
							} else {
								error('没有名为 ' + nameConvert + ' 的levelCommand指令');
							}
						}
					});

					if (!isEmptyObj(combine_data_son) || isEmptyObjectKept) {
						var propertys = property.split(rSplitComma);
						var isError;
						var isArr_combine_data = isArray(combine_data);
						if (isArrayCombine) {
							each(propertys, function (propName) {
								if (propName && !isArr_combine_data) {
									var it = combine_data[propName];
									if (it) {
										if (isArray(it)) {
											each(combine_data_son, function (item) {
												it.push(item);
											})
										} else {
											error('运行错误：名为“' + propName + '”的字段重复取值', item);
										}
									} else {
										combine_data[propName] = combine_data_son;
									}
								} else if (!propName && isArr_combine_data) {
									combine_data.push(combine_data_son);
								} else {
									isError = true;
									return false;
								}
							});
						} else {
							each(propertys, function (propName) {
								if (propName && !isArr_combine_data) {
									combine_data[propName] = combine_data_son;
								} else if (!propName && isArr_combine_data) {
									combine_data.push(combine_data_son);
								} else {
									isError = true;
									return false;
								}
							});
						}

						isError && error('语法错误：父子取值对象不匹配', item);;
					}

					if(willInterrup){
						interrupt();
						return false;
					}
				} else {//属性元素
					var aPropInfo = attr_combine.split(rSplitSemicolon);
					var property = aPropInfo[0];
					if (isExchanged) {
						var attrIndex = 2;
						var dataTypeIndex = 1;
					} else {
						var attrIndex = 1;
						var dataTypeIndex = 2;
					}
					//对应的多个采集属性
					var attrs4combine = (aPropInfo[attrIndex] || '').split(rSplitComma);
					//采集属性对应的类型转换函数
					var dataTypes = (aPropInfo[dataTypeIndex] || defaultValueConvert).split(rSplitComma);

					each(property.split(rSplitComma), function (prop4combine, i) {
						if (isInterrupt()) {
							return false;
						}
						var split_fragment = prop4combine.split(rSplitAt);
						var prop = split_fragment[0];

						var propExec = rArrItem.exec(prop);
						//采集目标是否为数组
						var isArrayPick = !!propExec;
						prop = isArrayPick ? propExec[1] : prop;

						var relies4prop = split_fragment[1];

						var isPick = true;
						//多依赖项处理
						relies4prop && (function () {
							//保证用户前后可以预留&或|
							var logicStr = relies4prop.replace(/[&|]+$/, '');

							var nextSymbol = '&';
							var exec_fragment;
							var nextContinue;
							var isTrue;
							while (exec_fragment = rLogic.exec(logicStr)) {
								var symbol = exec_fragment[3];

								var at0 = exec_fragment[1];
								var isNegation = at0 == '!';

								if (nextContinue) {
									//下次不需要continue;
									nextContinue = symbol === nextSymbol;
									continue;
								}

								var fn;
								var commandStr = (isNegation ? '' : at0) + exec_fragment[2].trim();

								var commandInfo = dealCommandStr(commandStr);
								var isCommand = commandInfo.isCommand;
								//执行的函数名称 非指令函数调用，转化成rely(attr)
								var fnName = commandInfo.name;

								if (!isCommand) {
									var reExec = rSplitEqualOrNo.exec(fnName);
									var relyArr;
									var isEq;
									if (reExec) {
										relyArr = fnName.split(rSplitEqualOrNo);
										isEq = reExec[1] == '=';
									} else {
										relyArr = [fnName];
										isEq = true;
									}
									var relyProp = relyArr[0];
									var relyValue = relyArr[2];

									fnName = 'rely';
								}

								isTrue = (fn = (relyCommand_opt[fnName] || relyCommand[fnName])) && executeCommand(item, fn, fnName, isCommand ? commandInfo.args : [relyProp, relyValue]);
								if (isInterrupt()) {
									isPick = false;
									break;
								}

								//非运算
								if (!isCommand) {
									isEq || (isTrue = !isTrue);
								}
								isNegation && (isTrue = !isTrue);

								if ((isTrue && symbol == '|') || (!isTrue && symbol == '&')) {
									nextContinue = true;
								} else {
									nextSymbol = symbol;
								}
							}

							isPick = isTrue;
						})();

						if (isPick) {
							//每个属性可以采集的多个值
							var attr4combine = attrs4combine[i] || '';
							each(attr4combine.split(rSplitOr), function (attr) {
								attr || (attr = defaultValuePick + '()');
								//需要优化，多次处理类似情况 dealCommandStr
								var oAttrCommandInfo = dealCommandStr(attr);
								var val;
								var isCommand = oAttrCommandInfo.isCommand;
								var fnAttrCommand;
								var nameAttrCommand = isCommand ? oAttrCommandInfo.name : defaultValuePick;
								if ((fnAttrCommand = (pickCommand_opt[nameAttrCommand] || pickCommand[nameAttrCommand]))) {
									val = executeCommand(item, fnAttrCommand, nameAttrCommand, isCommand ? oAttrCommandInfo.args : [attr]);
									if (isInterrupt()) {
										return false;
									}
								} else {
									error('没有名为 ' + nameAttrCommand + ' 的pickCommand指令');
								}


								var oConvertInfo = dealCommandStr(dataTypes[i] || defaultValueConvert);
								oConvertInfo.args.unshift(val);
								var fnConvert;
								var nameConvert = oConvertInfo.name;
								if ((fnConvert = (convertCommand_opt[nameConvert] || convertCommand[nameConvert]))) {
									val = executeCommand(item, fnConvert, nameConvert, oConvertInfo.args);
									if (isExtendedObject(val)) {
										Object.defineProperty(val, occupation, {
											get: function () {
												return 'convertCommand: customer-json';
											},
											configurable: occupationConfigurable,
										});
									}
									if (isInterrupt()) {
										return false;
									}
								} else {
									error('没有名为 ' + nameConvert + ' 的convertCommand指令');
								}

								if (!toContinue(val)) {
									if (isArrayPick && dataTypeIsArr && isArray(combine_data)) {
										combine_data.push(val);
									} else if (!(isArrayPick || dataTypeIsArr || isArray(combine_data))) {
										if (combine_data.hasOwnProperty(prop)) {
											error('运行错误：名为“' + prop + '”的字段重复取值', item);
										}
										combine_data[prop] = val;
									} else {
										error('语法错误：父子取值对象不匹配', item);
									}
									return false;
								}
							});
						}
					});
				}
			});
			return combine_data;
		};

		return _combine({
			srcData: opt.srcData,
			target: target
		});
	}

	//设置配置项
	combine.config = function (name, val) {
		if (isString(name)) {
			extendObj.config[name] = val;
		} else {
			var rEmpty = /\s/;
			for (var attr in name) {
				if (name.hasOwnProperty(attr) && extendObj.config[attr]) {
					val = name[attr];
					if (rEmpty.test(val)) {
						error('config标记中不能出现空白字符');
					}
					val = val.replace(/(\S)/g, '\$1');
					extendObj.config[attr] = val;
				}
			}
		}
	};
	//查看配置项的副本
	combine.showConfig = function () {
		return JSON.parse(JSON.stringify(extendObj.config));
	};
	//设置工具属性
	combine.store = createSetMethod('store');
	//设置“依赖器”
	combine.relyCommand = createSetMethod('relyCommand');
	//设置“取值器”
	combine.pickCommand = createSetMethod('pickCommand');
	//设置“转换器”
	combine.convertCommand = createSetMethod('convertCommand');
	//设置“层级处理器”
	combine.levelCommand = createSetMethod('levelCommand');

	//生成设置配置方法
	function createSetMethod(setName) {
		return function (name, fn) {
			if (isString(name)) {
				extendObj[setName][name] = fn;
			} else {
				for (var attr in name) {
					if (name.hasOwnProperty(attr)) {
						var item = name[attr];
						if (!isFunction(item)) {
							error(setName + '值须为函数');
						}
						extendObj[setName][attr] = item;
					}
				}
			}
		};
	}

	//是否为直接子集 保证复杂数据结构（有对象间嵌套）中目标元素只处理一次
	function notChildCombineDom(dom, upperDom, combineAttr, rParentObj) {
		var flag = false, parent, sCombine;
		while ((parent = dom.parentNode) !== upperDom && parent) {
			if ((sCombine = extendObj.config.getProp(parent, combineAttr)) != null && rParentObj.test(sCombine)) {
				flag = true;
				break;
			} else {
				dom = parent;
			}
		}
		return flag;
	}

	//参数固定用空白字符
	var argsSplit = /\s+/;
	//提取函数指令的信息
	function dealCommandStr(commandStr) {
		var re = {
			args: [],
			original: commandStr,
			isCommand: commandStr[commandStr.length - 1] === ')'
		};
		var exec_arr = rCommand.exec(commandStr);
		re.name = exec_arr[1];
		var _arg = exec_arr[2].trim();
		if (_arg) {
			re.args = _arg.split(argsSplit);
		}

		return re;
	}

	//创建包括空白符的分隔正则表达式
	function createSplitRe(str, noEscape) {
		var escape = noEscape ? '' : '\\';
		str = '\\s*' + escape + str + '\\s*';
		return new RegExp(str);
	}

	function each(arr, fn) {
		for (var i = 0, l = arr.length; i < l; i++) {
			if (fn(arr[i], i, arr) === false) {
				break;
			}
		}
	}

	function isEmptyObj(obj) {
		for (var t in obj) {
			if (obj.hasOwnProperty(t))
				return false;
		}
		return true;
	}

	var isExtendedObject = function (obj) {
		return obj !== null && typeof obj === 'object';
	};

	var isNullOrUndef = function (val) {
		return val == null;
	};

	var isString = isObjectType('String');
	var isArray = isObjectType('Array');
	var isFunction = isObjectType('Function');
	var isUndefined = isObjectType('Undefined');
	function isObjectType(type) {
		return function (obj) {
			return {}.toString.call(obj) === '[object ' + type + ']';
		};
	}

	function error(msg, log) {
		log && console.log(log);
		throw 'data-combine error : ' + msg;
	}

	if (typeof define === 'function') {//有可能未定义，不能用isFunction
		define('data-combine', [], function () {
			return combine;
		});
	} else {
		global.dataCombine || (global.dataCombine = combine);
	}

	return combine;
});

